/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.utils;

import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-4-26
 * Time: 0:58:26
 * String ipf = "211.92.137.1-211.92.137.55;211.92.137.*";
 * String ipaddress = "211.92.137.66";
 * <p/>
 * bindip.setExpression(ipf);
 * println("bindip.isIPFitter()=" + bindip.isIpExpression());
 * println("bindip.isIP()=" + bindip.isIpExpression(ipaddress);
 * println("bindip.interiorly()=" + bindip.interiorly(ipaddress));
 */

public class IpUtil
{
    final static private char[] ExpChar = new char[]{'@','#','$','%','^','<','>','&','+','\\','\'','\"'};
    private IpUtil()
    {

    }
    /**
     * @param expression 绑定ip地址
     * @return boolean 判断是否为一个合法 IP  绑定表达式
     */
    public static boolean isIpExpression(String expression)
    {
        if (expression == null) return false;
        if (expression.equals("*")) return true;
        if (expression.length() < 8) return false;
        for (char c:ExpChar)
        {
            if (expression.indexOf(c) != -1) return false;
        }
        return !(expression.startsWith("-") || expression.endsWith("-")) && StringUtil.split(expression, "\\.").length >= 3;
    }
    /**
     * @param aexp 判断一个表达式
     * @param theip ip地址
     * @return boolean   绑定IP地址用来绑定用户自己的IP
     */
    static private boolean oneInteriorly(String aexp, String theip)
    {
        if (StringUtil.isNULL(aexp) || aexp.equals("*")) return true;
        if (!StringUtil.isIPAddress(theip)) return false;
        if (aexp.equals(theip)) return true;
        String[] ipList = StringUtil.split(theip, "\\.");
        if (!aexp.contains("-"))
        {
            String[] sList = StringUtil.split(aexp, "\\.");
            for (int i = 0; i < ipList.length; i++)
            {
                if (!sList[i].equals("*") && StringUtil.toInt(sList[i]) != StringUtil.toInt(ipList[i]))
                {
                    return false;
                }
            }
            return true;
        } else
        {
            // 127.0.0.1-127.0.0.9
            String[] ips = StringUtil.split(aexp, "-");
            String[] alist1 = StringUtil.split(ips[0], "\\.");
            String[] blist2 = StringUtil.split(ips[1], "\\.");
            for (int i = 0; i < ipList.length; i++)
            {
                if (alist1[i].equals("*") || (StringUtil.toInt(ipList[i]) >= 0 && StringUtil.toInt(ipList[i]) >= StringUtil.toInt(alist1[i])))
                {

                } else
                {
                    return false;
                }
            }
            for (int i = 0; i < ipList.length; i++)
            {
                if (blist2[i].equals("*") || (StringUtil.toInt(ipList[i]) >= 0 && StringUtil.toInt(ipList[i]) <= StringUtil.toInt(blist2[i])))
                {
                } else
                {
                    return false;
                }
            }
            return true;
        }
    }

    /**
     * @param expression  表达式
     * @param ip ip地址
     * @return boolean 判断一个复合表达式
     */
    static public boolean interiorly(String expression,String ip)
    {
        if (!StringUtil.hasLength(expression)) return false;
        String fen = null;
        if (expression.contains(StringUtil.SEMICOLON))
        {
            fen = StringUtil.SEMICOLON;
        } else if (expression.contains(","))
        {
            fen = ",";
        } else if (expression.contains("/"))
        {
            fen = "/";
        }
        if (fen == null) return oneInteriorly(expression, ip);
        String[] ipList = StringUtil.split(expression, fen);
        for (String aIp : ipList)
        {
            if (oneInteriorly(aIp, ip)) return true;
        }
        return false;
    }


    /**
     * IP address to long
     *
     * @param aIpAddress 地址转换为长整数
     * @return Number representing an ip address
     */
    public static long toLong( String aIpAddress )
    {
        if ("localhost".equalsIgnoreCase(aIpAddress)) aIpAddress = "127.0.0.1";
        if (!aIpAddress.contains(".")) return 0;
        if (aIpAddress.contains(":")) aIpAddress = StringUtil.substringBefore(aIpAddress,":");
        long ip = 0;
        String[] t = aIpAddress.split("\\.");
        ip = Long.parseLong(t[0]) * 256 * 256 * 256;
        ip += Long.parseLong(t[1]) * 256 * 256;
        ip += Long.parseLong(t[2]) * 256;
        ip += Long.parseLong(t[3]);
        return ip;
    }


    /**
     * Long to IP address
     *
     * @param aIpAddress  地址转换为长整数
     * @return String representing an ip address
     */
    public static String getIPForLong( long aIpAddress )
    {
        return (aIpAddress >> 24 & 255) + "." + (aIpAddress >> 16 & 255) + "." + (aIpAddress >> 8 & 255) + "." + (aIpAddress & 255);
    }

    public static String getIp(InetAddress address)
    {
        String host = address.getHostAddress();
        if (host!=null && host.contains("/")) return StringUtil.substringAfter(host,"/");
        return host;
    }

    /**
     *
     * @param min 范围，最小
     * @param max 范围，最大
     * @return 得到有效的端口
     */
    public static int getAvailablePort(int min,int max)
    {
        PortTracker portTracker = new PortTracker();
        portTracker.setMinPort(min);
        portTracker.setMaxPort(max);
        try {
            return portTracker.getPort();
        } catch (Exception e) {
            return RandomUtil.getRandomInt(min,max);
        }
    }

    static class PortTracker
    {

        private int minPort = 16536;

        public int getMinPort()
        {
            return minPort;
        }

        public void setMinPort(int minPort)
        {
            this.minPort = minPort;
            this.port = minPort;
        }

        public int getMaxPort()
        {
            return maxPort;
        }

        public void setMaxPort(int maxPort)
        {
            this.maxPort = maxPort;
        }

        private int maxPort = 20536;

        private int port = 16536;

        public PortTracker()
        {

        }

        public synchronized int getPort() throws Exception {
            if (this.port >= this.maxPort)
            {
                this.port = this.minPort;
            }
            int p = this.port++;
            while (SystemUtil.isUsedPort(p))
            {
                p = this.port++;
            }
            return p;
        }

    }
    //-------------------------------------------------------------------------------------------------
    private static int ipc(byte b) {
        return (b >= 0) ? (int) b : ((int) b) + 256;
    }

    public static InetAddress publicIP() {

        try {
            InetAddress[] ia = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
            if (ia.length == 0) {
                try {
                    return InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    try {
                        return InetAddress.getByName("127.0.0.0");
                    } catch (UnknownHostException ee) {
                        return InetAddress.getLocalHost();
                    }
                }
            }
            if (ia.length == 1) {
                // only one network connection available
                return ia[0];
            }
            // we have more addresses, find an address that is not local
            int b0, b1;
            for (InetAddress anIa : ia) {
                b0 = ipc(anIa.getAddress()[0]);
                b1 = ipc(anIa.getAddress()[1]);
                if ((b0 != 10) && // class A reserved
                        (b0 != 127) && // loopback
                        ((b0 != 172) || (b1 < 16) || (b1 > 31)) && // class C reserved
                        (!anIa.getHostAddress().contains(":"))
                        ) {
                    return anIa;
                }
            }
            // there is only a local address, we filter out the possibly returned loopback address 127.0.0.1
            for (InetAddress anIa : ia) {
                if ((ipc(anIa.getAddress()[0]) != 127) &&
                        (!anIa.getHostAddress().contains(":"))) {
                    return anIa;
                }
            }
            // if all fails, give back whatever we have
            for (InetAddress anIa : ia) {
                if (!anIa.getHostAddress().contains(":")) return anIa;
            }
            return ia[0];
        } catch (java.net.UnknownHostException e) {
            System.err.println("ERROR: (internal) " + e.getMessage());

        }
        try {
            return InetAddress.getLocalHost();
        } catch (Exception e) {
            try {
                return InetAddress.getByName("localhost");
            } catch (UnknownHostException e1) {
                e1.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取本机 IPV6  地址
     * @return
     * @throws IOException
     */
    public static String getLocalIPv6Address() throws IOException {
        InetAddress inetAddress = null;
        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
                .getNetworkInterfaces();
        outer:
        while (networkInterfaces.hasMoreElements()) {
            Enumeration<InetAddress> inetAds = networkInterfaces.nextElement()
                    .getInetAddresses();
            while (inetAds.hasMoreElements()) {
                inetAddress = inetAds.nextElement();
                //Check if it's ipv6 address and reserved address
                if (inetAddress instanceof Inet6Address
                        && !isReservedAddr(inetAddress)) {
                    break outer;
                }
            }
        }

        String ipAddr = inetAddress.getHostAddress();
        // Filter network card No
        int index = ipAddr.indexOf('%');
        if (index > 0) {
            ipAddr = ipAddr.substring(0, index);
        }

        return ipAddr;
    }

    /**
     * Check if it's "local address" or "link local address" or
     * "loopbackaddress"
     *
     *
     * @return result
     */
    private static boolean isReservedAddr(InetAddress inetAddr) {
        if (inetAddr.isAnyLocalAddress() || inetAddr.isLinkLocalAddress()
                || inetAddr.isLoopbackAddress()) {
            return true;
        }

        return false;
    }

}